package com.iteaj.iot.client.http;

import com.iteaj.iot.FrameworkManager;
import com.iteaj.iot.ProtocolException;
import com.iteaj.iot.ProtocolHandle;
import com.iteaj.iot.client.ClientProtocol;
import com.iteaj.iot.client.IotClient;
import com.iteaj.iot.consts.ExecStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;

public abstract class HttpClientProtocol<M extends HttpClientMessage> implements ClientProtocol<M> {

    /**
     * 要请求的设备的设备编号
     */
    private String deviceSn;

    /**
     * 默认同步调用
     */
    private boolean sync = true;

    /**
     * 自定义协议处理
     */
    private ProtocolHandle handle;

    /**
     * 执行状态
     */
    private ExecStatus status;

    /**
     * 失败原因
     */
    private String reason;

    /**
     * 请求报文
     */
    private M requestMessage;

    /**
     * 响应报文
     */
    private M responseMessage;

    /**
     * 协议额外携带的参数
     */
    private Map<String, Object> param;

    private Logger logger = LoggerFactory.getLogger(getClass());

    public HttpClientProtocol(M requestMessage) {
        this.requestMessage = requestMessage;
    }

    /**
     * @return 当前协议对象
     * @throws ProtocolException
     */
    public HttpClientProtocol request() throws HttpProtocolException {
        doBuildRequestMessage();
        if(requestMessage == null) {
            throw new HttpProtocolException("构建Http协议请求报文失败, 请求报文不能未空");
        }

        if(requestMessage.getUrl() == null) {
            throw new HttpProtocolException("必须指定请求的Url地址");
        }

        if(this.handle == null) {
            this.handle = FrameworkManager.getProtocolHandle(getClass());
        }

        try {
            HttpClientComponent clientComponent = FrameworkManager.getClientComponent(requestMessage.getClass());
            HttpClient client = clientComponent.getClient(new HttpClientConnectProperties(requestMessage.getUrl()));
            if(client == null) {
                throw new HttpProtocolException("请先创建客户端["+requestMessage.getUrl()+"]");
            }

            if(requestMessage.getMethod() == HttpMethod.Get) {
                if(isSync()) {
                    client.get(requestMessage);
                } else {
                    client.get(requestMessage, message -> handle.handle(this));
                }
            } else {
                if(isSync()) {
                    client.post(requestMessage);
                } else {
                    client.get(requestMessage, message -> handle.handle(this));
                }
            }
        } catch (Exception e) {
            Throwable cause = e.getCause();
            if(cause instanceof SocketTimeoutException) {
                this.status = ExecStatus.timeout;
                logger.error("连接超时", cause);
            } else if(cause instanceof ConnectException) {
                this.status = ExecStatus.fail;
                logger.error("连接失败", cause);
            } else if(cause instanceof NoRouteToHostException) {
                this.status = ExecStatus.notWritable;
                logger.error("找不到主机, 请检查访问地址", cause);
            } else {
                this.status = ExecStatus.fail;
                logger.error("未知错误", cause);
            }
        }

        /**
         * 如果是同步的話, 先解析报文, 然后再执行业务
         */
        if(this.isSync()) {
            try {
                if(requestMessage.isOK()) {
                    this.status = ExecStatus.success;
                } else {
                    this.status = ExecStatus.fail;
                }

                this.responseMessage = this.doBuildResponseMessage(requestMessage);
                if(handle != null) {
                    handle.handle(this);
                }

                return this;
            } catch (Exception e) {
                throw new HttpProtocolException("解析设备报文错误", e);
            }
        } else {
            return null;
        }
    }

    @Override
    public IotClient getIotClient() {
        return null;
    }

    /**
     *
     * @param handle 自定义此协议的处理器
     * @return 当前协议对象
     * @throws ProtocolException
     */
    public HttpClientProtocol request(HttpProtocolHandler handle) throws ProtocolException {
        this.handle = handle;
        if(this.handle == null) {
            throw new ProtocolException("参数必填[handle]");
        }

        return this.request();
    }

    /**
     * 异步调用
     * @param handle
     * @return
     */
    public HttpClientProtocol async(HttpProtocolHandler handle) {
        this.handle = handle; this.sync = false;
        return this;
    }

    public boolean isSync() {
        return sync;
    }

    public Object getParam(String key) {
        if(null == this.param) {
            return null;
        }

        return this.param.get(key);
    }

    public HttpClientProtocol addParam(String key, Object value) {
        if(this.param == null) {
            this.param = new HashMap<>();
        }
        this.param.put(key, value);
        return this;
    }

    @Override
    public HttpClientProtocol removeParam(String key) {
        if (this.param != null) {
            this.param.remove(key);
        }

        return this;
    }

    @Override
    public String desc() {
        return "Http客户端请求协议";
    }

    public Map<String, Object> getParam() {
        return param;
    }

    /**
     * 构建请求报文
     * @return
     */
    protected abstract M doBuildRequestMessage();

    /**
     * 构建响应报文
     * @return
     */
    protected abstract M doBuildResponseMessage(M message);

    @Override
    public M requestMessage() {
        return this.requestMessage;
    }

    @Override
    public M responseMessage() {
        return this.responseMessage;
    }

    protected void setRequestMessage(M requestMessage) {
        this.requestMessage = requestMessage;
    }

    protected void setResponseMessage(M responseMessage) {
        this.responseMessage = responseMessage;
    }

    @Override
    public String getEquipCode() {
        return this.deviceSn;
    }

    public void setDeviceSn(String deviceSn) {
        this.deviceSn = deviceSn;
    }

    public ExecStatus getStatus() {
        return status;
    }

    public HttpClientProtocol setStatus(ExecStatus status) {
        this.status = status; return this;
    }

    public ProtocolHandle getHandle() {
        return handle;
    }

    public HttpClientProtocol setHandle(ProtocolHandle handle) {
        this.handle = handle; return this;
    }

    public String getReason() {
        return reason;
    }

    public HttpClientProtocol setReason(String reason) {
        this.reason = reason; return this;
    }
}
